#include <ksetup.h>
#include <i386.h>
#include <elf.h>
#include <frame.h>
#include <pci.h>
#include <mboot.h>


#define	KERNEL_SPACE_START	0xc0000000

void ksetup_mboot( uint32 mbd, uint32 magic )
{
	if ( magic != 0x2BADB002 )
	{
		/* Something went not according to specs. Print an error */
		/* message and halt, but do *not* rely on the multiboot */
		/* data structure. */
	}
	mboot_info = (struct mboot_info_s *) mbd;
	
	init_tty();
	clear();
	puts("Maestro 0.1.01, starting setup...\n\n");
	//uint32 edx;
	//asm volatile("cpuid":"=d"(edx):"a" (0x1));
	//kprintf("CPUID_EDX:%X\n", edx);
	
	
	kprintf("MIS_FLAGS: 0x%X\n", mboot_info->flags);
	if(mboot_info->flags & MBOOT_MEM_SIZE_INFO){
		kprintf("Lower memory: %i kilobytes, and upper memory: %i kilobytes.\n", mboot_info->mem_lower, mboot_info->mem_upper);
	}
	uint64 addr = 0, length = 0;
	if(mboot_info->flags & MBOOT_MMAP_INFO){
		kprintf("Mmap fields are present (addr: 0x%X, length: %i).\n", mboot_info->mmap_addr, mboot_info->mmap_length);
		mboot_mmap_info = (struct mboot_mmap_info_s *)(mboot_info->mmap_addr);
		for(;(uint32)mboot_mmap_info < (uint32)mboot_info->mmap_addr + (uint32)mboot_info->mmap_length;
			mboot_mmap_info = (struct mboot_mmap_info_s *)((uint32)mboot_mmap_info->size + (uint32) sizeof(mboot_mmap_info->size) + (uint32)mboot_mmap_info)){
			kprintf("Size: 0x%X, type: 0x%X\n", mboot_mmap_info->size, mboot_mmap_info->type);
			addr = mboot_mmap_info->base_addr;
			length = mboot_mmap_info->length;
			kprintf("Addr: 0x%X %X, length: %X %X\n", (uint32)(addr >> 32), (uint32)(addr & 0xffffffff), (uint32)(length >> 32), (uint32)(length & 0xffffffff));
		}
	}
	for(;;);
}

void
ksetup(setup_init_t *setup_init, acpi_mem_map_t *acpi_mm, uint32 mmsize, void *ramfs, uint32 ramfs_size){
	uint32 setup_init_addr = 0, z = 0;
	Elf32_Ehdr *kehdr;
	Elf32_Phdr *phdr;
	uint32 kfend;
	struct xdtr_reg_t *gdtr, old_gdtr; 
	//for(;;);
	init_tty();
	//for(;;);
	clear();
	puts("Maestro 0.1.01, starting setup...\n\n");
	uint32 edx;
	asm volatile("cpuid":"=d"(edx):"a" (0x1));
	kprintf("CPUID_EDX:%X\n", edx);
	//for(;;);
	/*
	struct pci_info_s pci_info;
	struct bios32_sd_s *bios32 = find_bios32();
	addr_t *pcibios_entery;
	int dev, d_nr;
	if(bios32){
		pcibios_entery = (addr_t *)find_pcibios(bios32);
		is_pcibios_exist(pcibios_entery, &pci_info);
		for(d_nr = 0; d_nr < 0xFFFFFF; d_nr++){
			dev = find_pci_class_code(d_nr, 0);
			//if(dev < 0) kprintf("Erro[%i]: %X\n", d_nr, -dev);
			//else 
			if(dev >= 0) kprintf("Dev[%i]: %X\n", d_nr, dev);
		}
		kprintf("END\n");
		for(;;);
	}else{
		kprintf("Error: BIOS32 not found\n");
		for(;;);
	}
	*/
	
	
	kprintf("RAMFS: %X, RAMFS_SIZE: %i\n", ramfs, ramfs_size);

	kprintf("ACPI_MMAP_ADDR: %X, ACPI_MMAP_SIZE: %X\n", (uint32) acpi_mm, (uint32) mmsize);

	kprintf("SETUP_INIT: %X, K_ELF_SIZE: %X, K_ELF_START: %X, K_ELF_END: %X, K_INIT_MAGIC: %X\n", setup_init, setup_init->ksize, setup_init->kstart, setup_init->kend, setup_init->magic);
	
	int i;
	for(i = 0; i < mmsize;i++){
		kprintf("BLOCK[%i]: START:%X, SIZE: %X, TYPE: %X, ATTR: %X\n", i, (uint32)acpi_mm[i].start, (uint32)acpi_mm[i].length, acpi_mm[i].type, acpi_mm[i].attr);
	}
	
	
	//kfend = KFSTART + setup_init->ksize;
	kehdr = (Elf32_Ehdr *) setup_init->kstart;
	uint32 entry_addr = kehdr->e_entery;
	kprintf("e_phoff: %X, e_shoff: %X, e_phnum: %X, e_entery: %X\n", kehdr->e_phoff, kehdr->e_shoff, kehdr->e_phnum, kehdr->e_entery);
	phdr = (Elf32_Phdr *) (setup_init->kstart + kehdr->e_phoff);
	
	for(z = 0; z < kehdr->e_phnum; z++){
		kprintf("p_vaddr: %X, p_filesz: %X, p_memsz: %X, p_offset: %X\n", phdr[z].p_vaddr, phdr[z].p_filesz, phdr[z].p_memsz, phdr[z].p_offset);
		if(phdr[z].p_memsz != 0)
		memcp((uint32)kehdr + phdr[z].p_offset, phdr[z].p_vaddr - KVAZ, phdr[z].p_filesz);
	}
		
	kfend = (phdr[0].p_vaddr - KVAZ) + phdr[0].p_memsz; //Конец ядра
	kprintf("KERNEL_VEND: %X %X\n", kfend, phdr[0].p_vaddr);
	//for(;;);
	
	//Устанавливаем карту сразуже за ядром
	frame_map = (frame_t *) ((kfend + 0xfff) & 0xFFFFF000);
	//Инициализируем распределитель физической памяти
	uint32 frame_map_size = init_frame_mem(acpi_mm, mmsize, frame_map);
	kprintf("MEM_SIZE = %iMb\n", frame_map_size * 4096 /1024/1024);
	
	/*
	 * Помечаем как зарезервированные кадры занятые ядром и картой фреймов
	 */
	uint32 resv = (uint32)(frame_map + frame_map_size);
	kprintf("RES: %i\n", resv / 1024 / 1024);
	for(i = 0x100000 / 4096; i * 4096 <= resv; i++){
		frame_map[i].reserved = 1;
	}
	kprintf("Fmem OK\n");
	
	
	/*Инициализируем виртуальную память. Первые 4 мегабайта и 4 мегабайта начиная
	 *с адреса 0xC0000000 отображаем в первые 4 мегабайта физической памяти
	 */ 
	
	page_t *kernel_PDE = (page_t *) init_mm();
	kprintf("PDE: %X\n", kernel_PDE);
	uint32 tmp_esp;
	asm("movl %%esp, %0":"=r" (tmp_esp));
	tmp_esp += 0xc0000000;
	kprintf("ESP: %X\n", tmp_esp);
	asm("movl %0,%%esp"::"r"(tmp_esp));	
	
	
	//Адрес глобальной таблицы дескрипторов GDT
	gdtable = (struct seg_descriptor_t *)allocFrame(2);
	kprintf("GDT: %X\n", gdtable);
	//Обнуляем ее
	uint32 *zgdt = (uint32 *)gdtable;
	for(z = 0; z < GDT_SIZE / 4; z++){
		zgdt[z] = 0;
	}
	//for(;;);
	//Устанавливаем нужные дескрипторы
	set_descriptor(0,0,0,0,0);
	set_descriptor(12, SCODE_XR, 0, 0xFFFFF, SPL);
	set_descriptor(13, SDATA_RW, 0, 0xFFFFF, SPL);
	set_descriptor(14, SCODE_XR, 0, 0xFFFFF, UPL);
	set_descriptor(15, SDATA_RW, 0, 0xFFFFF, UPL);
	gdtable = (struct seg_descriptor_t *)((uint32)gdtable + 0xc0000000);
	gdtr = (struct xdtr_reg_t *)((uint32)gdtable + GDT_SIZE);
	kprintf("GDTR: %X\n", gdtr);
	gdtr->xr_addr[0] = GDT_SIZE;
	gdtr->xr_addr[1] = (uint16)((uint32) gdtable);
	gdtr->xr_addr[2] = (uint16) (((uint32)gdtable) >> 16);
	
	asm("lgdt %0"::"m"(*gdtr));
	
	asm("pushfl");
	asm("pushl $0x60");
	asm("pushl $new_regs");
	
	asm("iret");
	asm("new_regs:");
	asm("movw $0x68, %ax; movw %ax, %ds; movw %ax, %fs; movw %ax, %es; movw %ax, %gs; movw %ax, %ss;");
	
	void ** kernel_setup = (void **) allocFrame(1);
	uint32 ksetup_size = 0;
#define set_env(addr)	\
	kernel_setup[ksetup_size++]	= (void *) (addr);
	
	set_env((uint32)frame_map + KERNEL_SPACE_START);
	set_env(frame_map_size);
	set_env((uint32)kernel_PDE + KERNEL_SPACE_START);
	set_env((uint32)gdtable);
	kprintf("GDT: %X\n", gdtable);
	printsegaddr(12);
	printsegaddr(13);
	set_env(ramfs);
	set_env(ramfs_size);	
	
	kernel_setup = (void **)((uint32)kernel_setup + KERNEL_SPACE_START);
	asm("pushl %0"::"m"(kernel_setup));
	asm("pushl %0"::"m"(ksetup_size));
	//asm("cli");
	//Передаем управление kernel_start()
	asm("jmp *%0"::"m" (kehdr->e_entery));
	for(;;){}
	
}

